<?php
/**
 * @todo Refractor internal names.  There are way too many methods with
 *    "smart_search" or "SmartSearch" as part of their names.  They need to
 *    define what they are doing instead of what they are being used by.
 */
class C_PatientFinder extends Controller {

    var $template_mod;
    var $_db;
    var $join_db;
    var $limit = 100;
    var $showNonPatients = false;
    var $patient;
    var $dateformat;

    function C_PatientFinder($template_mod = "general") {
        $GLOBALS['C_MAIN']['current_controller'] = "PatientFinder";
        parent::Controller();
        if (Celini::getCurrentController() == 'PatientFinder') {
            $menu =& Menu::getInstance();
            $menu->currentSection = 'patient';
        }
        $this->_db = $GLOBALS['frame']['adodb']['db'];
        $this->template_mod = $template_mod;
        $this->view->path = 'patient_finder';
        // TODO: @LOCALIZATION
        $this->dateformat = L10nHelper::getSqlDateFormat();
    }

    function actionSearch(){
        return $this->view->render('search.html');
    }

    function actionSearchAjax(){
        // Is it smart search?
        switch ($_GET['search_type']){
            case 'smart_search':	/* Diep: Change this into quick search */
                $_GET['searchstring'] = Utils::sanitizeText($_GET['searchstring']);
                $resultSearch = $this->buildQuickSearchQuery($_GET['searchstring']);
                return $this->buildQuickSearchResultAjax($resultSearch, Celini::link('viewMedicalRecord','Patient',true));
                break;
            case 'daily_search':
                $daily['daily_code'] = Utils::sanitizeText($_GET['daily_code']);
                $daily['daily_name'] = Utils::sanitizeText($_GET['daily_name']);
                $query = $this->buildDailySearchQuery($daily,$this->showNonPatients);
                $resultSearch = $this->_db->GetAll($query);
                return $this->buildTableDailyResultAjax($resultSearch, Celini::link('viewMedicalRecord', 'Patient', true));
                break;
            case 'demographic_search':
                // Get search parameter then pass to search function
                $this->patient['code'] = Utils::sanitizeText($_GET['code']);
                $this->patient['name'] = Utils::sanitizeText($_GET['name']);
                if ($_GET['female'] != "false") {
                    $this->patient['gender'] = "female";
                } elseif ($_GET['male'] != "false"){
                    $this->patient['gender'] = "male";
                } else $this->patient['gender'] = "";
                $this->patient['date'] = Utils::sanitizeText($_GET['date']);
                $this->patient['phone'] = Utils::sanitizeText($_GET['phone']);
                $this->patient['email'] = Utils::sanitizeText($_GET['email']);
                $this->patient['career'] = Utils::sanitizeText($_GET['career']);
                $this->patient['street'] = Utils::sanitizeText($_GET['street']);
                $this->patient['ward'] = Utils::sanitizeText($_GET['ward']);
                $this->patient['district'] = Utils::sanitizeText($_GET['district']);
                $this->patient['province'] = Utils::sanitizeText($_GET['province']);
                $query = $this->buildDemographicsQuery();
                $resultSearch = $this->_db->GetAll($query);
                return $this->buildTableResultAjax($resultSearch, Celini::link('viewMedicalRecord','Patient',true));

                break;
        }
    }

    private function buildTableResultAjax($result_set, $patient_action){
        $ajax_result['header'] = "<table class='grid' cellspacing='0' cellpadding='0' style='width: 100%'>
			<thead><tr>
			<th style='width: 7%'>" . L10nHelper::_("Patient's ID_1") . "</th>
			<th style='width: 15%'>" . L10nHelper::_("Full name") . "</th>
			<th style='width: 9%'>" . L10nHelper::_("Date of birth_1") . "</th>
			<th style='width: 7%'>" . L10nHelper::_("Gender") . "</th>
			<th style='width: 12%'>" . L10nHelper::_("Phone number_1") . "</th>
			<th style='width: 15%'>" . L10nHelper::_("Email") . "</th>
			<th style='width: 35%'>" . L10nHelper::_("Address") . "</th></tr></thead>";
        $ajax_result['body'] = "<tbody>";
        if (!empty($result_set)){
            foreach ($result_set as $result){
                $dob = L10nHelper::renderDateTime($result['date'],'medium');
                $result['address']	=	L10nHelper::getAddress($result['province'], $result['district'], $result['ward'], $result['street']);
                //print_r($result);
                ($result['gender'] == 'male') ? $gender = L10nHelper::_("Male") : $gender = L10nHelper::_("Female");
                if (strpos($result['address'], ',') == 0) {
                    $result['address'] = substr($result['address'], 1);
                }
                $ajax_result['body'] .= "<tr>
					<td>{$result['code']}</td>
					<td><a href='{$patient_action}patientId={$result['id']}'>{$result['name']}</a></td>
					<td style='overflow: hidden; white-space: nowrap'>{$dob}</td>
					<td>{$gender}</td>
					<td>{$result['phone']} &nbsp;</td>
					<td style='overflow: hidden; white-space: nowrap'>{$result['email']} &nbsp;</td>
					<td>{$result['address']}</td></tr>";

            }
            $ajax_result['body'] .= "</tbody></table>";
            $ajax_result['canSort'] = 1;
            //print_r($ajax_result);

        } else {
            $ajax_result['body'] .= "<tr><td colspan='8'>" . L10nHelper::_("Not found the result") . "</td></tr></tbody></table>";
            $ajax_result['canSort'] = 0;
        }
        echo json_encode($ajax_result);
    }

    private function buildTableDailyResultAjax($result_set, $patient_action){
        $ajax_result['header'] = "<table class='grid' cellspacing='0' cellpadding='0' style='width: 100%'>
			<thead><tr>
			<th style='width: 7%'>" . L10nHelper::_("Patient's ID_1") . "</th>
			<th style='width: 15%'>" . L10nHelper::_("Full name") . "</th>
			<th style='width: 9%'>" . L10nHelper::_("Date of birth_1") . "</th>
			<th style='width: 7%'>" . L10nHelper::_("Gender") . "</th>
			<th style='width: 15%'>" . L10nHelper::_("Enter time") . "</th>
			<th style='width: 12%'>" . L10nHelper::_("Phone number_1") . "</th>
			<th style='width: 15%'>" . L10nHelper::_("Email") . "</th>
			<th style='width: 20%'>" . L10nHelper::_("Address") . "</th></tr></thead>";
        $ajax_result['body'] = "<tbody>";
        //var_dump($result_set);
        if (!empty($result_set)){
            foreach ($result_set as $result){
                $dob = L10nHelper::renderDateTime($result['date'],'medium');
                $result['address']	=	L10nHelper::getAddress($result['province'], $result['district'], $result['ward'], $result['street']);
                ($result['gender'] == 'male') ? $gender = L10nHelper::_("Male") : $gender = L10nHelper::_("Female");
                if (strpos($result['address'], ',') == 0) {
                    $result['address'] = substr($result['address'], 1);
                }
                $ajax_result['body'] .= "<tr>
					<td>{$result['code']}</td>
					<td><a href='{$patient_action}patientId={$result['id']}'>{$result['name']}</a></td>
					<td style='overflow: hidden; white-space: nowrap'>{$dob}</td>
					<td>{$gender}</td>
					<td style='overflow: hidden; white-space: nowrap'>{$result['created']} &nbsp;</td>
					<td>{$result['phone']} &nbsp;</td>
					<td style='overflow: hidden; white-space: nowrap'>{$result['email']} &nbsp;</td>
					<td>{$result['address']}</td></tr>";
            }
            $ajax_result['body'] .= "</tbody></table>";
            $ajax_result['canSort'] = 1;
        } else {
            $ajax_result['body'] .= "<tr><td colspan='8'>" . L10nHelper::_("Not found the result") . "</td></tr></tbody></table>";
            $ajax_result['canSort'] = 0;
        }
        echo json_encode($ajax_result);
    }

    private function buildDailySearchQuery($condition, $showNonPatients=false){
        // @LOCALIZATION
        $today = date('Y-m-d');
        // @LOCALIZATION
        $field		=	L10nHelper::changeField(L10nHelper::getLocale(), 'name');
        $dateTimeFormat	=	L10nHelper::getSqlDateFormat('YmdHi');

        $query = "SELECT psn.person_id AS id,
					psn.name AS name,
					date_of_birth as date,
					patient.patient_code AS code,
					gender,
					DATE_FORMAT(t.created, '$dateTimeFormat') as created,
					phone_number AS phone,
					email,
					address.street, address.ward_name ward, district.$field district, province.$field province
				FROM person psn
					INNER JOIN patient ON (psn.person_id = patient.person_id)
					INNER JOIN ticket t ON (t.patient_id = psn.person_id)
					LEFT JOIN address ON (psn.address_id = address.address_id)
					LEFT JOIN province ON (address.province_id = province.province_id)
					LEFT JOIN district ON (address.district_id = district.district_id)					
				 WHERE (t.department_id = '".$this->_me->get_department_id()."') AND (t.created LIKE '".$today."%')
		";
        $query .= $this->buildDailyConditionQueryPart($condition).
				" GROUP BY code
				ORDER BY psn.name
				LIMIT {$this->limit}";
        return $query;
    }

    private function buildDailyConditionQueryPart($condition){
        // FIXME: @LOCALIZATION
        $where = "";
        if ($condition['daily_code'] != "") $where .= " AND (patient.patient_code LIKE '".$condition['daily_code']."%') ";
        if ($condition['daily_name'] != "") $where .= " AND (psn.name LIKE '%".$condition['daily_name']."%') ";
        return $where;
    }

    private function buildDemographicsQuery(){
        // @LOCALIZATION
        $field	=	L10nHelper::changeField(L10nHelper::getLocale(), 'name');
        $query = "SELECT psn.person_id AS id,
					psn.name AS name,
					date_of_birth as date,
					pt.patient_code AS code,
					gender,
					phone_number AS phone,
					email,
					address.street, address.ward_name ward, district.$field district, province.$field province
			    FROM person psn
					INNER JOIN patient AS pt ON (psn.person_id = pt.person_id)
					LEFT JOIN address ON (psn.address_id = address.address_id)
					LEFT JOIN province ON (address.province_id = province.province_id)
					LEFT JOIN district ON (address.district_id = district.district_id)					
				 WHERE ";
        $query .= $this->buildDemographicsConditionQueryPart()
        . " GROUP BY code
			ORDER BY psn.name
			LIMIT {$this->limit}";
        return $query;
    }

    private function buildDemographicsConditionQueryPart(){
        // @LOCALIZATION
        $field	=	L10nHelper::getLocale();
        $sql = "true";
        if ($this->patient['code'] !== "") $sql = $sql. " AND (pt.patient_code LIKE '%".$this->patient['code']."%')";
        if ($this->patient['name'] !== "") $sql = $sql. " AND (psn.name LIKE '%".$this->patient['name']."%')";
        if ($this->patient['gender'] !== "") $sql = $sql. " AND (psn.gender = '".$this->patient['gender']."')";
        if ( preg_match('/^(\d{1,2})[\/-](\d{1,2})[\/-](\d{2,4})$/', $this->patient['date'])
        || preg_match('/^(\d{1,2})[\/-](\d{2,4})$/', $this->patient['date'])
        || preg_match('/^(\d{4})$/', $this->patient['date'])) {
            $xdate = &new DateObject();
            $searchdate = L10nHelper::getISODate($this->patient['date']);
            $sql = $sql . " AND (psn.date_of_birth = '$searchdate.')";
        } elseif ($this->patient['date'] !== "") {
            $sql = $sql . " AND psn.date_of_birth = -1";
        }
        if ($this->patient['phone'] !== "") $sql = $sql. " AND (psn.phone_number = '".$this->patient['phone']."')";
        if ($this->patient['email'] !== "") $sql = $sql. " AND (psn.email LIKE '%".$this->patient['email']."%')";
        if ($this->patient['street'] !== "") $sql = $sql. " AND (address.street LIKE '%".$this->patient['street']."%')";
        if ($this->patient['ward'] !== "") $sql =  $sql. " AND (address.ward_name LIKE '%".$this->patient['ward']."%')";
        if ($this->patient['district'] !== "") $sql = $sql. " AND (l1.$field LIKE '%".$this->patient['district']."%')";
        if ($this->patient['province'] !== "") $sql = $sql. " AND (l2.$field LIKE '%".$this->patient['province']."%')";
        return $sql;
    }

    private function buildSmartSearchQuery($search_string, $showNonPatients=false) {
        // @LOCALIZATION
        $this->showNonPatients = $showNonPatients;
        $join_type = "INNER";
        if ($this->showNonPatients !== false) {
            $join_type = "LEFT";
        }

        $field	=	L10nHelper::changeField(L10nHelper::getLocale(), 'name');
        $sql = "SELECT psn.person_id AS id,
				psn.name AS name,
				DATE_FORMAT(date_of_birth,'{$this->dateformat}') AS date,
				pt.patient_code AS code,
				gender,
				phone_number AS phone,
				email,
				address.street, address.ward_name ward, district.$field district, province.$field province
			FROM person psn
			$join_type JOIN patient AS pt ON (psn.person_id = pt.person_id)
				LEFT JOIN address ON (psn.address_id = address.address_id)
				LEFT JOIN province ON (address.province_id = province.province_id)
				LEFT JOIN district ON (address.district_id = district.district_id)
			WHERE ";

			$cleanedValue = mysql_real_escape_string($search_string);
			$sqls = $this->buildSmartConditionQueryPart($search_string);

			if(count($sqls) == 0){
			    return(array('','Invalid Search'));
			}

			$sqland = $sql .'('. $sqls[0]. ")
			     GROUP BY code
			     ORDER BY psn.name
			     LIMIT {$this->limit}";
			$result_array = $this->_db->GetAll($sqland);
			if (sizeof($result_array) > 0) {
			    return $result_array;
			}

			if ($this->showNonPatients) {
			    $person =& ORDataObject::factory('Person');
			    foreach($result_array as $key => $row) {
			        if (empty($row['person_type'])) {
			            $row['person_type'] = 1;
			        }
			        $result_array[$key]['person_type'] = $person->lookupType($row['person_type']);
			        $result_array[$key]['string'] = $result_array[$key]['name'] .'('.$result_array[$key]['person_type'].')';
			    }
			}
			return($result_array);
    }

    /**
     * Returns array of sql items to put in the WHERE clause
     * @param string $search_string space-separated list of items for smart search
     *
     * @todo Update the formatting of this so it matches the Uversa coding
     *    standards (see wiki).
     * @todo Remove all ereg code and replace with preg_match
     * @todo Consider wholesale refractoring.  Each of this various if()
     *    statements contain code that could possibly be (or maybe is being) used
     *    elsewhere in CH.  A perfect example is the date checking.  All of that
     *    is already handled in DateObject, so there's no need to do it all again
     *    (now using dateobject)
     * @todo Determine if all of the if()s should be mutually exclusive, or
     *    should you attempt to match as many as you can guess
     */
    private function buildSmartConditionQueryPart($search_string){
        // @LOCALIZATION

        //print 'Begin smart search\n';
        $GLOBALS['namesearch']=false;
        //var_dump($search_string);
        if (preg_match('/([a-z0-9]+), ?([a-z0-9]+)/i', $search_string, $matches)) {
            $searcharray = $matches;
            array_shift($searcharray);
        } else {
            $searcharray=explode(" ",$search_string);
            array_unshift($searcharray,$search_string);
        }
        $xdate=new DateObject();

        for($x=0; $x<count($searcharray); $x++){
            $searcharray[$x]=trim($searcharray[$x]);
            if(preg_match('/^(\d{1,2})[\/-](\d{1,2})[\/-](\d{2,4})$/', $searcharray[$x])
            || preg_match('/^(\d{1,2})[\/-](\d{2,4})$/', $searcharray[$x])
            || preg_match('/^(\d{4})$/', $searcharray[$x])){
                // Date of birth
                $searchdate = $xdate->createFromVN($searcharray[$x]);
                $sqls[]="(date_of_birth LIKE '%".$searchdate->toISO()."')";

                if (preg_match('/^[0-9]+$/', $searcharray[$x])) {
                    // Phone number contains only number
                    $sqls[] = "(pt.patient_code LIKE '%" . (int)$searcharray[$x] . "%' OR phone_number LIKE '%" . (int)$searcharray[$x]."%')";
                }
            } elseif (preg_match('/^[0-9]+$/', $searcharray[$x])) {
                // Phone number contains only number
                $sqls[] = "(pt.patient_code LIKE '%" . (int)$searcharray[$x] . "%' OR phone_number LIKE '%" . (int)$searcharray[$x]."%')";
            } elseif (preg_match('/^.+@.+?$/', $searcharray[$x])) {
                // Email address
                $searchval = $searcharray[$x];
                $sqls[] = "(email LIKE '%" .$searchval."%')";
            } else {
                // Regular name or address
                $GLOBALS['namesearch']=true;
                $cleanedValue = mysql_real_escape_string($searcharray[$x]);
                $cleanedValue = str_replace(array(',', '  '), ' ', $cleanedValue);
                $sqls[]="(psn.name LIKE '%".$cleanedValue."%')".
						" OR (CONCAT(address.street, ', ', address.ward_name, ', ', district.name, ', ', province.name) LIKE '%".$cleanedValue."%')";
            }
        }
        return($sqls);
    }

    private function buildQuickSearchQuery($search_string){
        // @LOCALIZATION
        $field	=	L10nHelper::changeField(L10nHelper::getLocale(), 'name');
        $sql = "SELECT psn.person_id AS id, psn.name AS name, date_of_birth AS date,
				pt.patient_code AS code, gender, phone_number AS phone, address.street, address.ward_name ward, 
				province.$field district, province.$field province
			FROM person psn INNER JOIN patient AS pt ON (psn.person_id = pt.person_id)
				LEFT JOIN address ON (psn.address_id = address.address_id)
				LEFT JOIN province ON (address.province_id = province.province_id)
				LEFT JOIN district ON (address.district_id = district.district_id)
			WHERE ";
        if (preg_match('/^[0-9]+$/', $search_string)) {
            $sql .= "pt.patient_code LIKE '%" . (int)$search_string . "%'";
        } else {
            $cleanedValue = mysql_real_escape_string($search_string);
            $cleanedValue = str_replace(array(',', '  '), ' ', $cleanedValue);
            $sql .= "psn.name LIKE '%".$cleanedValue."%'";
        }
        $sql .= 'ORDER BY psn.name
				LIMIT 20';
        return $this->_db->GetAll($sql);
    }

    private function buildQuickSearchResultAjax($result_set, $patient_action){
        $ajax_result['header'] = "<table class='grid' cellspacing='0' cellpadding='0' style='width: 100%'>
			<thead><tr>
			<th style='width: 10%'>" . L10nHelper::_("Patient's ID_1") . "</th>
			<th style='width: 20%'>" . L10nHelper::_("Full name") . "</th>
			<th style='width: 15%'>" . L10nHelper::_("Date of birth_1") . "</th>
			<th style='width: 10%'>" . L10nHelper::_("Gender") . "</th>
			<th style='width: 15%'>" . L10nHelper::_("Phone number_1") . "</th>
			<th style='width: 25%'>" . L10nHelper::_("Latest prescription") . "</th></tr></thead>";
        $ajax_result['body'] = "<tbody>";
        if (!empty($result_set)){
            foreach ($result_set as $result){
                $dob = L10nHelper::renderDateTime($result['date'],'medium');
                $result['address']	=	L10nHelper::getAddress($result['province'], $result['district'], $result['ward'], $result['street']);
                //print_r($result);
                ($result['gender'] == 'male') ? $gender = L10nHelper::_("Male") : $gender = L10nHelper::_("Female");
                if (strpos($result['address'], ',') == 0) {
                    $result['address'] = substr($result['address'], 1);
                }
                $ajax_result['body'] .= "<tr>
						<td class='first' style='border-bottom: none;'>{$result['code']}</td>
						<td style='border-bottom: none;'><a href='{$patient_action}patientId={$result['id']}'>{$result['name']}</a></td>
						<td style='border-bottom: none;'>{$dob}</td>
						<td style='border-bottom: none;'>{$gender}</td>
						<td style='border-bottom: none;'>{$result['phone']} &nbsp;</td>
						<td style='border-bottom: none;' class='last'>
							<a href='javascript:viewLatestPrescription({$result['id']})'>". L10nHelper::_("Latest prescription")."</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
						</td>
					</tr>
					<tr>
						<td colspan='6' style='border-top: none; padding-left: 30px; color: #666'>" . L10nHelper::_("Address: &nbsp;&nbsp;&nbsp;&nbsp; %s",$result['address'])."</td>
					</tr>
					";
            }
            $ajax_result['body'] .= "</tbody></table>";
            $ajax_result['canSort'] = 1;
        } else {
            $ajax_result['body'] .= "<tr><td colspan='6'>" . L10nHelper::_("Not found the result") . "</td></tr></tbody></table>";
            $ajax_result['canSort'] = 0;
        }
        echo json_encode($ajax_result);
    }

    public function actionFastSearchPatientCode() {

        $result = array('error'=>array('code'=>CoreErrorCode::NORMAL,'mess'=>''),
        'result'=>'');
        $patientCode = $_POST['patientCode'];
        $controller = $_POST['controller'];
        $actionName = $_POST['actionName'];
        $redirect = $_POST['redirect'];
        
        //$patientCode = str_pad($patientCode, 6, "0", STR_PAD_LEFT);

        $query = "SELECT person_id FROM patient WHERE patient_code = '" . $patientCode . "' LIMIT 1";
        $resultDb = $this->_db->GetAll($query);

        try{
            if (!empty($resultDb)) {
                if ($redirect == 1) {
                    //$redirectLink = Celini::link('viewMedicalRecord','Patient') . "patientId=" . $result['0']['person_id'];
                    $rs = Celini::link($actionName, $controller) . "patientId=" . $resultDb['0']['person_id'];
                } else {
                    $hisLoader = new HISFileLoader();
                    $controllerObj = $hisLoader->getObj('C_' . $controller);
                    $action = 'action' . $actionName;
                    $rs = $controllerObj->$action($resultDb['0']['person_id']);
                }
                $result['result'] =  $rs;
            } else {
                $result['error']['code'] = 'NOTFOUND';
            }
        } catch (HISFileLoaderException $hisEx) {
            $result['error']['code'] = 'NOTFOUND';
        }

        return json_encode($result);
    }

    public function actionFastSearchPatientName() {

        $result = array('error'=>array('code'=>CoreErrorCode::NORMAL,'mess'=>''),
        'result'=>'');
        $patientName = $_POST['patientName'];
        $patientObj = Celini::newOrdo('Patient');
        $rs = $patientObj->searchPatientByName($patientName);
        if (!empty($rs)) {
            foreach ($rs as &$item) {
            	$item['date_of_birth'] = L10nHelper::renderDateTime($item['date_of_birth']);
            	$item['address'] = Celini::newOrdo('Person', $item['person_id'])->getAddress();
            }
            $result['result'] = $rs;
        } else {
            $result['error']['code'] = 'NOTFOUND';            
        }
        return json_encode($result);
    }

    public function actionFastSearchPatientNameAfter() {

        $result = array('error'=>array('code'=>CoreErrorCode::NORMAL,'mess'=>''),
        'result'=>'');
        $patientId = trim($_POST['patientId']);
        $controller = $_POST['controller'];
        $actionName = $_POST['actionName'];
        try{
            if ($patientId != '') {
                $hisLoader = new HISFileLoader();
                $controllerObj = $hisLoader->getObj('C_' . $controller);
                $action = 'action' . $actionName;
                $rs = $controllerObj->$action($patientId);
                $result['result'] =  $rs;
            } else {
                $result['error']['code'] = 'NOTFOUND';
            }
        } catch (HISFileLoaderException $hisEx) {
            $result['error']['code'] = 'NOTFOUND';
        }

        return json_encode($result);
    }
}
?>
